package com.xcity.game.combat;

import java.util.ArrayList;
import java.util.List;
import java.util.PriorityQueue;
import java.util.Queue;
import java.util.Random;
import java.util.concurrent.atomic.AtomicInteger;

import org.slf4j.Logger;

import com.xcity.game.combat.listener.ActionListener;
import com.xcity.game.combat.listener.CombatListener;
import com.xcity.game.combat.listener.LifeCycleListener;
import com.xcity.game.combat.unit.CombatUnit;

import naga.x.common.Updatable;

/**
 * 一场战斗(pvp/pve)
 * @author yang.li
 *
 */
public class Combat implements Updatable, LifeCycleListener {

	protected int id;
	protected Type type;
	protected CombatTeam source, target;
	protected List<Round> rounds; // 回合记录
	protected State state = State.INIT;
	protected CombatListener listener;
	
	public static final byte SUCC = 1, FAIL = 0;
	public static final Random RND = new Random();
	protected static final AtomicInteger IDS = new AtomicInteger();
	protected static final Logger LOG = CombatService.LOG;
	
	public static enum Type {
		PVP, // 攻打建筑
		PVE, // 拓展（谈判）
		ARENA_OFFLINE, // 离线竞技场
		ARENA_ONLINE, // 在线竞技场
		RESOURCE_INSTANCE, // 资源副本（时尚广场）
		PEAK_ROAD, // 爬塔副本（巅峰之路）
		MINE_TAKE, // 占矿
		MINE_ATTACK, // 抢矿
		WORLD_BOSS, // 世界boss
		;
	}
	
	public static enum State {
		INIT, COMBATING, SOURCE_WON, TARGET_WON
	}
	
	public Combat(CombatTeam source, CombatTeam target, Type type, CombatListener listener) {
		this.id = IDS.incrementAndGet();
		this.type = type;
		this.source = source;
		this.target = target;
		this.rounds = new ArrayList<Round>();
		this.listener = listener;
		this.state = State.COMBATING;
		init();
		LOG.info("[COMBAT NEW]ID[{}]TYPE[{}]SOURCE[{}]TARGET[{}]", id, type, source.getLeader().getId(), target.getLeader().getId());
	}
	
	private void init() {
		source.init();
		target.init();
	}
	
	private Queue<CombatUnit> initActionQueue() {
		// 初始化战斗顺序
		Queue<CombatUnit> queue = new PriorityQueue<CombatUnit>(size());
		queue.addAll(source.all());
		queue.addAll(target.all());
		return queue;
	}
	
	/**
	 * 获取该单位攻击目标
	 * @param unit
	 * @return
	 */
	public CombatUnit getHitTarget(CombatUnit unit) {
		return unit.getTeam() == source ? target.getDefenseUnit(unit.getIndex()) : source.getDefenseUnit(unit.getIndex());
	}
	
	public CombatTeam getHitTargetTeam(CombatUnit unit) {
		return unit.getTeam() == source ? target : source;
	}
	
	/**
	 * 获取当前出手单位
	 */
	private CombatUnit getHitUnit(Queue<CombatUnit> queue) {
		CombatUnit u = queue.poll();
		if (u != null && u.isDied()) {
			return getHitUnit(queue);
		}
		return u;
	}
	
	public CombatUnit getUnitRandomly() {
		CombatUnit u = source.getUnitRandomly();
		CombatUnit u2 = target.getUnitRandomly();
		if (u == null) {
			return u2;
		}
		if (u2 == null) {
			return u;
		}
		return RND.nextBoolean() ? u : u2;
	}
	
	protected Round getLastRound() {
		return rounds.size() > 0 ? rounds.get(rounds.size() - 1) : null;
	}
	
	protected Round getRound(int index) {
		if (rounds.size() > index) {
			return rounds.get(index);
		}
		if (rounds.size() == index && !isEnd()) {
			return nextRound();
		}
		return null;
	}
	
	private Round nextRound() {
		Round round = new Round(this, rounds.size());
		Queue<CombatUnit> queue = initActionQueue();
		roundCalc(round, queue, null);
		rounds.add(round);
		return round;
	}
	
	/**
	 * 计算一回合战斗
	 * @param round
	 * @param listener
	 */
	protected void roundCalc(Round round, Queue<CombatUnit> queue, ActionListener listener) {
		if (round.actionIndex == 0) {
			roundStart(round);
		}
		for (;;) {
			CombatUnit unit = getHitUnit(queue);
			if (unit == null) {
				break;
			}
//			unit.setState(CombatUnit.State.ACTIONING);
			Action action = new Action(unit);
			action.target = getHitTargetTeam(unit);
			round.add(action);
//			action.addTarget(getHitTarget(unit));
			action.execute(listener);
			if (isEnd()) {
				break;
			}
		}
		roundEnd(round);
	}
	
	private void roundStart(Round round) {
		roundStart(round, source.all());
		roundStart(round, target.all());
		listener.roundStart(round);
	}
	
	private void roundStart(Round round, List<CombatUnit> units) {
		for (int i = 0; i < units.size(); i++) {
			CombatUnit u = units.get(i);
			if (!u.isDied()) {
				u.roundStart(round);
			}
		}
	}
	
	private void roundEnd(Round round) {
		roundEnd(round, source.all());
		roundEnd(round, target.all());
		listener.roundEnd(round);
	}
	
	private void roundEnd(Round round, List<CombatUnit> units) {
		for (int i = 0; i < units.size(); i++) {
			CombatUnit u = units.get(i);
			if (!u.isDied()) {
				u.roundEnd(round);
			}
		}
	}
	
	public int size() {
		return source.size() + target.size();
	}
	
	public boolean isEnd() {
		if (state == State.INIT || state == State.COMBATING) {
			if (source.isDied()) {
				state = State.TARGET_WON;
			} else if (target.isDied()) {
				state = State.SOURCE_WON;
			} else if (roundCount() >= CombatUtils.Config.roundsMax) {
				state = State.TARGET_WON;
			} else if (isTimeout()) {
				state = State.TARGET_WON;
			}
		}
		return state == State.SOURCE_WON || state == State.TARGET_WON;
	}
	
	public boolean isWinner(long unitId) {
		CombatUnit unit = find(unitId);
		if (unit == null) {
			return false;
		}
		CombatTeam team = unit.getTeam();
		if (team == source) {
			return state == State.SOURCE_WON;
		}
		return state == State.TARGET_WON;
	}
	
	public List<Round> getRounds() {
		return rounds;
	}
	
	public int roundCount() {
		return rounds.size();
	}
	
	public State getState() {
		return state;
	}

	public CombatTeam getSource() {
		return source;
	}

	public CombatTeam getTarget() {
		return target;
	}

	@Override
	public boolean update() {
		return true;
	}
	
	public CombatUnit find(long unitId) {
		CombatUnit unit = source.find(unitId);
		if (unit != null) {
			return unit;
		}
		unit = target.find(unitId);
		return unit;
	}
	
	public int getId() {
		return id;
	}
	
	public void end(String cause) {
		end(cause, true);
	}

	public void end(String cause, boolean notify) {
		listener.end(this, notify);
		LOG.info("[COMBAT END]ID[{}]TYPE[{}]SOURCE[{}]TARGET[{}]RESULT[{}]ROUNDS[{}]CAUSE[{}]", id, type, source.getLeader().getId(), target.getLeader().getId(), state, roundCount(), cause);
		
		// DEBUG LOG
//		for (int i = 0; i < rounds.size(); i++) {
//			Round r = rounds.get(i);
//			r.actionIndex = 0;
//		}
//		LOG.info("[COMBAT ROUNDS]ID[{}]ROUNDS[{}]", id, JSON.toJSONString(rounds, SerializerFeature.DisableCircularReferenceDetect));
	}
	
	@Override
	public void died(CombatUnit unit) {
	}

	@Override
	public void relive(CombatUnit unit) {
	}
	
	public boolean isTimeout() {
		return false;
	}
	
}
